﻿using System.Collections.Concurrent;
using RpcHelper;
using RpcModel.RemoteLock;
using RpcSync.Service.Interface;

namespace RpcSync.Service.RemoteLock
{
    internal class LockController
    {
        private IRemoteLockConfig _Config;
        public LockController(string lockId, IRemoteLockConfig config)
        {
            this._Config = config;
            this.LockId = lockId;
        }

        private readonly ConcurrentDictionary<string, LockSession> _LockSession = new ConcurrentDictionary<string, LockSession>();

        /// <summary>
        /// 锁Id
        /// </summary>
        public string LockId
        {
            get;
        }

        /// <summary>
        /// 当前会话
        /// </summary>
        private string _CurrentId = null;
        private readonly LockHelper _Lock = new LockHelper();
        #region 申请锁
        public ApplyLockRes ApplyLock(ApplyLock apply, long serverId)
        {
            LockSession rlock = this._ApplyLock(apply);
            if (rlock == null)
            {
                throw new ErrorException("lock.apply.fail");
            }
            return rlock.ApplyLock(serverId);
        }
        private LockSession _ApplyLock(ApplyLock apply)
        {
            if (this._CurrentId != null && this._LockSession.TryGetValue(this._CurrentId, out LockSession rlock))
            {
                return rlock;
            }
            else if (this._Lock.GetLock())
            {
                if (this._CurrentId == null)
                {
                    rlock = new LockSession(apply,this._Config);
                    if (this._LockSession.TryAdd(rlock.SessionId, rlock))
                    {
                        this._CurrentId = rlock.SessionId;
                        this._Lock.Exit();
                        return rlock;
                    }
                }
                this._Lock.Exit();
                return null;
            }
            else
            {
                return null;
            }
        }

        #endregion

        internal ApplyLockRes GetLockStatus(GetLockStatus obj)
        {
            if (this._LockSession.TryGetValue(obj.SessionId, out LockSession rlock))
            {
                return rlock.GetLockStatus();
            }
            throw new ErrorException("lock.session.not.find");
        }

        public void ReleaseLock(ReleaseLock obj, long serverId)
        {
            if (this._LockSession.TryGetValue(obj.SessionId, out LockSession rlock))
            {
                rlock.ReleaseLock(serverId, obj.Extend);
                return;
            }
            throw new ErrorException("lock.session.not.find");
        }

        public bool CheckLock()
        {
            if (this._LockSession.Count > 0)
            {
                int time = HeartbeatTimeHelper.HeartbeatTime;
                string[] keys = this._LockSession.Keys.ToArray();
                keys.ForEach(a =>
                {
                    if (this._LockSession.TryGetValue(a, out LockSession rlock))
                    {
                        if (rlock.IsEnd && rlock.ReleaseTime < time)
                        {
                            if (this._LockSession.TryRemove(a, out rlock))
                            {
                                rlock.Dispose();
                            }
                        }
                        else if (rlock.CheckLock(time) && rlock.OverTime < time && this._CurrentId == rlock.SessionId)
                        {
                            this._CurrentId = null;
                        }
                    }
                });
                return false;
            }
            else
            {
                return true;
            }
        }
    }
}
